iT邦幫忙

2023 iThome 鐵人賽

DAY 24
1

在 JavaScript 中,this 關鍵字是一個相當重要且常被誤解的概念,對初學者來說是大魔王般的存在,即使是老手有時也會不小心誤判結果。以函式來說,它的值取決於函式是如何呼叫的,並且在不同的區域中,this 的行為可能不同。實際上要立刻完全弄懂 this 的規則是不容易的,而在這篇文章中,將會先探討 this 關鍵字的常見使用情境。

在開始說明如何判斷 this 拿到的值前,我們要先知道 this 是甚麼。簡單來說,this 這個關鍵字代表的值為目前執行環境綁定的實體。例如在全域執行環境中,this 通常指向全域物件,但這是在非嚴格模式下的結果。

// 在非嚴格模式下,
// 1. 在瀏覽器中,輸出為 window 物件
// 2. 在 node.js 環境中,輸出為 global
console.log(this);
"use strict";

console.log(this); // 嚴格模式下,輸出為 undefined

接下來預設都會以瀏覽器環境的非嚴格模式來舉例,請注意在嚴格模式的結果可能有所不同。


傳統函式中的 this

初學者可能會認為函式中的 this 會指向該函式,但這是錯誤的概念。實際上在一個函式中,this 與函式如何宣告沒有關係,僅與呼叫方式有關。this 的值取決於呼叫該函式的物件。直接來看例子。

pet1 = 'dog'; // 會在全域物件中新增 pet1 屬性
var pet2 = 'cat'; // 會在全域物件中新增 pet2 屬性
let pet3 = 'rabbit'; // 不會在全域物件中新增屬性

function showPet() {
  console.log(this.pet1);
  console.log(this.pet2);
  console.log(this.pet3);
}

 showPet(); // 依序輸出 'dog' 'cat' undefined

上例是在全域中呼叫 showPet(),所以 showPet 裡面的 this 會指向全域物件。


var name = 'Apple';

function showName() {
  console.log(this.name);
}

const person = {
  name: 'Banana',
  showName: showName
};

showName(); // 輸出 'Apple'。這裡的 showName 在全域中,所以 this 指向全域物件
person.showName(); // 輸出 'Banana'。這裡呼叫 person 物件下的 showName,所以 this 指向 person

var anotherFn = person.showName;
anotherFn(); // 輸出 'Apple'。這裡的 anotherFn 在全域中,所以 this 指向全域物件

我們來看更複雜的情況,以下是在一些方法的回調函式中,this 的範例。

const numbers = [1, 2, 3];
numbers.forEach(function (num) {
  console.log(num, this); // 這裡的 forEach 的 callback function,this 是全域物件 window
});
const numbers = [1, 2, 3];

const myObj = {
  prop: 'Hello',
  sayHello: function () {
    numbers.forEach(function (num) {
      console.log(num, this); // 呼叫 myObj.sayHello() 時,這裡 this 指向 myObj
    }, this);
  }
};

myObj.sayHello();

但是在非同步方法的回調函式中,this 指向全域物件。

const myObj = {
  prop: 'Hello',
  sayHello: function () {
    setTimeout(function () {
      console.log(this); // this 指向全域物件(在瀏覽器中通常是 window)
    }, 1000);
  }
};

myObj.sayHello(); // 一秒後輸出 window

箭頭函式中的 this

箭頭函式沒有自己的 this,我們只需判斷外層執行環境的 this 值

直接拿上一個例子改用箭頭函式看會有甚麼差別。

我們先將 setTimeout 的 callback 改用箭頭函式:

const myObj = {
  prop: 'Hello',
  sayHello: function () {
    setTimeout(() => {
      console.log(this); 
    }, 1000);
  }
};

myObj.sayHello(); // 一秒後輸出 myObj 物件

這個例子中,setTimeout 的 callback 是箭頭函式,所以它的 this 由外層決定,也就是 sayHello 的執行環境。而 sayHello 是傳統函式,它的 this 由呼叫方式決定,因此透過「myObj.sayHello()」呼叫,this 就會指向 myObj。


我們再把 sayHello 也改用箭頭函式:

const myObj = {
  prop: 'Hello',
  sayHello: () => {
    setTimeout(() => {
      console.log(this); 
    }, 1000);
  }
};

myObj.sayHello(); // 一秒後輸出全域物件 window

這個例子中,setTimeout 的 callback 是箭頭函式,所以它的 this 由外層決定,也就是 sayHello 的執行環境。而 sayHello 也是箭頭函式,它的 this 也由外層決定,也就是全域執行環境,因此這裡的 this 會指向 全域物件。


結語

今天舉例了 this 的應用及如何判斷它的值,不過本篇文章只舉例了我認為在開發時比較常見的情況,若要瞭解更詳細的規則可參考MDN文件。實際上我們也可以手動改變 this 綁定的參考物件,就留到隔天談吧。那今天就到這邊,明天見~


上一篇
[Day 23] JavaScript 中的函式
下一篇
[Day 25] 函式原型中的 apply()、bind()、call()
系列文
複習 JavaScript 核心概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言